package com.cskaoyan.javase.oop3._2inner._6lambda._1basic;

import java.util.IdentityHashMap;

/**
 * Lambda表达式的简化:
 * Lambda表达式能够简化的前提是：
 * 它是功能接口的子类对象，而功能接口中有且只有一个必须要实现的抽象方法！
 * 正是由于以上前提,Lambda表达式的下列简化,才不会带来歧义
 * Lambda表达式的语法
 * (形参列表) -> {
 *  // 方法体
 * }
 * 逐一简化上述语法中的结构:
 * 1."(形参列表)"能不能简化呢?
 *      可以简化的,形参的数据类型,写与不写都不会影响Lambda表达式的使用
 *      所以形参的数据类型是可以简化的
 *      但是形参的名字不能简化,可以和抽象方法中的形参名不一致,但是不能去掉
 *      特殊的,如果形参只有一个,那么连带数据类型和"()"可以一起去掉,只写形参名
 *      当然,如果方法是无参的,"()"是不能去掉的
 *
 * 2."{}"能不能简化呢?
 *      在Lambda表达式的语法中,我们最希望简化的是"{}"
 *      那么它如何去掉呢?
 *      当"{}"当中仅有一条语句时,那么"{}"可以去掉
 *      特殊的,当"{}"的仅有一条语句且该条语句就是返回值语句时,那么"{}"和return一起省略
 *
 *  以上简化,在开发中,最希望被简化的是"{}"
 *  但是实际开发方法重写往往比较复杂,一行代码就写完Lambda表达式的方法重写,是很少见的情况
 *  那么在这样的情况下,还想去掉"{}",还想一行写完Lambda表达式,保持Lambda表达式的简洁和优雅,怎么办呢?
 *  Lambda表达式，本质上就是重写了一个抽象方法的子类对象
 *  所以Java允许Lambda表达式的抽象方法的实现可以直接指向一个已经存在的方法，而不是自己书写实现。
 *  这种语法在Java中称之为"方法引用"！
 *
 *  在具体讲解"方法引用"的语法之前,我们先思考一个重要问题:
 *  什么样的方法可以作为功能接口中抽象方法的实现,并用Lambda表达式指向它呢?
 *  假如对于以下功能接口中的抽象方法:
 *  public abstract 返回值类型 方法名(形参列表);
 *
 *  那么这个已实现的方法,要求:
 *      1.访问权限修饰符有没有要求?
 *          没有要求,只需要在使用该方法的位置有权限就够了(私有方法除非在同类下,否则是不行的)
 *      2.static还是非static?有要求吗?
 *          没有要求,只不过static方法类名点可以直接调用,但是成员方法需要创建对象再调用
 *      3.返回值类型有没有要求呢?
 *          有要求,这个已实现的方法相当于重写抽象方法
 *          所以如果抽象方法的返回值类型是void和基本数据类型,必须保持一致
 *          但是如果抽象方法返回父类类型,那么该实现方法可以保持一致也可以返回子类类型
 *      4.方法名有没有要求呢?
 *          没有要求,可以保持一致,也可以随便
 *      5.形参列表有没有要求呢?
 *          有要求,必须保持一致,但是形参的名字无所谓
 *      6.方法体有没有要求?
 *          没有要求,无所谓,按照需求来写方法体的实现即可.
 *
 * 以上,功能接口中抽象方法的实现方法已经具备后,开始根据以下"方法引用"的语法来写Lambda表达式,来创建功能接口的子类对象.
 *
 * "方法引用"的语法,有两种形式:
 *      1.(了解,不推荐) 不省略"->"的形式
 *      (形参列表) -> 已存在方法的方法调用(形参列表);
 *      解释:
 *          a.语法左右的"形参列表"要保持一致,是照抄抽象方法的形参列表.
 *          b."已存在方法的方法调用"意思是:
 *          在定义Lambda表达式的位置,去调用这个指向的已实现方法,如果是静态方法,就用"类名."调用
 *          如果是成员方法,就用对象调用.
 *
 *      2.(推荐)省略"->"的形式
 *      Lambda表达式就可以按照以下格式去写:
 *      "方法的归属者::方法名;"
 *      解释:
 *          "::"是由两个冒号组成的
 *          方法的归属者:
 *          static方法属于类
 *          成员方法属于对象
 *
 *
 * @since 15:06
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        INoReturnTwoParam ip = (int num, int num2) -> {
            System.out.println(num + num2);
        };
        // 以上Lambda表达式简化为
        INoReturnTwoParam ip2 = (num, num2) -> {
            System.out.println(num + num2);
        };
        ip2.test(10, 10);

        INoReturnOneParam ip3 = a -> {
            System.out.println("a的值是:" + a);
        };
        ip3.test(666);

        INoReturnNoParam ip4 = () -> {
            System.out.println("我创建了接口INoReturnNoParam的子类对象!");
        };
        // 以上Lambda表达式简化为
        INoReturnNoParam ip5 = () -> System.out.println("我创建了接口INoReturnNoParam的子类对象!");

        IHasReturnNoParam ip6 = () -> 777;

        // 使用"->"形式的方法引用
        INoReturnOneParam ip7 = a -> myTest(a);
        ip7.test(666);

        // 省略"->"的方法引用
        INoReturnOneParam ip8 = Demo::myTest;
        ip8.test(777);

        // 使用"->"形式的方法引用
        IA ia = (num1,num2) -> new Demo().test(num1, num2);
        System.out.println(ia.test(100, 100));

        // 省略"->"的方法引用
        IA ia2 = new Demo()::test;
        System.out.println(ia2.test(200, 200));
    }

    // 定义一个方法,作为INoReturnOneParam功能接口中抽象方法test的实现
    public static void myTest(int num) {
        System.out.println("----------------");
        System.out.println("我是接口INoReturnOneParam的抽象方法实现");
        System.out.println(num);
        System.out.println("----------------");
    }

    // 定义一个方法,作为功能接口IA中抽象方法test的实现
    private String test(int a, int num) {
        System.out.println("================");
        System.out.println(a + num);
        System.out.println("今天晚上准备去吃大餐!");
        System.out.println("================");
        return "你好世界!";
    }
}

@FunctionalInterface
interface IA {
    Object test(int a, int b);
}

//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}
